use serde::{Deserialize, Serialize};
use zeroize::{Zeroize, ZeroizeOnDrop};

use crate::{BlockChainBaseError, constants::PRIVATE_KEY_BYTES_LENGTH};

#[derive(Zeroize, ZeroizeOnDrop, Serialize, Deserialize, Debug, PartialEq, Clone)]
pub struct PrivateKeyBytes([u8; PRIVATE_KEY_BYTES_LENGTH]);

impl std::fmt::Display for PrivateKeyBytes {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}", self.to_hex_str())
    }
}

impl PrivateKeyBytes {
    pub fn new(bytes: [u8; PRIVATE_KEY_BYTES_LENGTH]) -> Self {
        Self(bytes)
    }

    pub fn from_slice(slice: &[u8; PRIVATE_KEY_BYTES_LENGTH]) -> Self {
        Self(*slice)
    }

    pub fn from_hex_str(s: &str) -> Result<Self, BlockChainBaseError> {
        let bytes = hex::decode(s)?;
        if bytes.len() != PRIVATE_KEY_BYTES_LENGTH {
            return Err(BlockChainBaseError::PrivatekeyBytesHexStrLenInvalid);
        }

        Ok(Self(bytes.try_into().unwrap()))
    }

    pub fn to_hex_str(&self) -> String {
        hex::encode(self.0)
    }

    pub fn length(&self) -> usize {
        PRIVATE_KEY_BYTES_LENGTH
    }

    pub fn as_bytes_ref(&self) -> &[u8; PRIVATE_KEY_BYTES_LENGTH] {
        &self.0
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_serde_bincode_privkey_bytes() {
        #[rustfmt::skip]
        let data: [u8; PRIVATE_KEY_BYTES_LENGTH] = [
            1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
            17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32,
        ];

        let privkey = PrivateKeyBytes::new(data);
        let serialized = bincode::serialize(&privkey).unwrap();
        let deserialized: PrivateKeyBytes = bincode::deserialize(&serialized).unwrap();
        assert_eq!(privkey, deserialized);
    }

    #[test]
    fn test_private_key_bytes_drop() {
        let key_ptr: *const u8 = {
            let key = PrivateKeyBytes::new([0xbc; PRIVATE_KEY_BYTES_LENGTH]);
            let key_ptr: *const u8 = key.0.as_ptr(); // Get a raw pointer to the key's memory

            let data = unsafe { std::slice::from_raw_parts(key_ptr, PRIVATE_KEY_BYTES_LENGTH) };
            println!("Memory before drop: {:?} @ {:?}", data, key_ptr);
            assert_eq!(data, &[0xbc; PRIVATE_KEY_BYTES_LENGTH]);

            key_ptr
        };

        let data = unsafe { std::slice::from_raw_parts(key_ptr, PRIVATE_KEY_BYTES_LENGTH) };
        println!("Memory after drop: {:?} @ {:?}", data, key_ptr);
        assert_eq!(data, &[0; PRIVATE_KEY_BYTES_LENGTH])
    }
}
